Project 2 (EAS 509: Fall’23)

Project Title: Time Series Analysis & Forecasting of Oil Sales

Team Members:

  1. Sujay Shrivastava (50496221) (sujayshr)
  2. Utkarsh Mathur (50495131) (umathur)
  3. Venkata Lakshmi Krishna Tejaswi Gudimetla (50496378) (vgudimet)

Importing Libraries and Data

# Visualization and Analysis
library(dplyr)
library(tidyr)
library(ggplot2)
library(ggdendro)
library(readxl)
library(plotly)
suppressWarnings({
  library(readxl)
})

# Modeling and Inference
library(TSA)
library(forecast)
library(astsa)
# Importind Data.
# NOTE: Please store the oil.csv file in the directory of this file.
df = read.csv('oil.csv')
summary(df)
     date             dcoilwtico    
 Length:1218        Min.   : 26.19  
 Class :character   1st Qu.: 46.41  
 Mode  :character   Median : 53.19  
                    Mean   : 67.71  
                    3rd Qu.: 95.66  
                    Max.   :110.62  
                    NA's   :43      

From the above summary we can observe that there are 1218 datapoints in out dataset and 43 missing values. We will address the missing values later on.


Plotting Original Data

plot1 <- df |>
  plot_ly(type="scatter", mode="lines") |>
  add_trace(x = ~date, y = ~dcoilwtico, name="Daily Sale") |>
  layout(showlegend=FALSE, plot_bgcolor = "white")
options(warn=-1)

plot1
Warning: Can't display both discrete & non-discrete data on same axisWarning: Can't display both discrete & non-discrete data on same axis

Imputing Missing Value

Based on the analysis of the plot we have decided to impute missing value with the next value observed in the time series.

df_new <- df %>% fill(dcoilwtico, .direction="up")
summary(df_new)
     date             dcoilwtico    
 Length:1218        Min.   : 26.19  
 Class :character   1st Qu.: 46.42  
 Mode  :character   Median : 53.19  
                    Mean   : 67.67  
                    3rd Qu.: 95.59  
                    Max.   :110.62  

We now plot the imputed dataset.

plot2 <- df_new |>
  plot_ly(type="scatter", mode="lines") |>
  add_trace(x = ~date, y = ~dcoilwtico, name="Daily Sale") |>
  layout(showlegend=FALSE, plot_bgcolor = "white")
options(warn=-1)

plot2
Warning: Can't display both discrete & non-discrete data on same axisWarning: Can't display both discrete & non-discrete data on same axis

Upon plotting the imputed dataset we can observe that there is no trend or seasonality observed over time. This is a very good instance to test Exponential Smoothing methods.

components_df <- decompose(df_new)
Error in decompose(df_new) : time series has no or less than 2 periods

As it can be seen in the above code piece the data has no seasonality as the time series in not decomposible.


ETS and Holt-Winters Models

Forecasts produced using exponential smoothing methods are weighted averages of past observations, with the weights decaying exponentially as the observations get older. In other words, the more recent the observation the higher the associated weight.

Simple Exponential Smoothing

The simplest of exponentially smoothing methods is Simple Exponential Smoothing (SES). This method is suitable for forecasting data with no clear trend or seasonal pattern. For simple exponential smoothing, the only component included is the level as observed in the below forecasting and smoothing equation. The smoothing equation only uses alpha to include the trend component of Time Series.

Holt-Winters Methods

Holt (1957) and Winters (1960) extended Holt's method to capture seasonality. The Holt-Winters seasonal method comprises the forecast equation and three smoothing equations — one for the level \(l_t\) one for the trend \(b_t\), and one for the seasonal component \(s_t\) ,with corresponding smoothing parameters \(\alpha\), \(\beta^*\), and \(\gamma\) respectively.

Additive Method:

Multiplicative Method:


Model Training

As the data doesn’t have seasonality in it (evident from the decomposition of data) we can safely say that Holt-Winters method is of lesser use here. So we are going to compare 2 models:

  1. ARIMA (trained without specifying p, d, and q)
  2. Exponential Smoothing

ARIMA Model

modelARIMA <- auto.arima(df_new['dcoilwtico'])
summary(modelARIMA)
Series: df_new["dcoilwtico"] 
ARIMA(0,1,0) 

sigma^2 = 1.449:  log likelihood = -1952.37
AIC=3906.73   AICc=3906.73   BIC=3911.83

Training set error measures:
                      ME     RMSE       MAE         MPE     MAPE      MASE        ACF1
Training set -0.03759184 1.203095 0.8949041 -0.08030733 1.547907 0.9992644 -0.04614975

As observed, the best fit ARIMA model is ARIMA(p=0, d=1, q=0) with AICc3906.73 and RMSE 1.203095.

fcARIMA <- forecast(modelARIMA, h=100)
autoplot(fcARIMA)

Exponential Smoothing model

dfETS <- as.ts(df_new['dcoilwtico'])
modelETS <- ets(dfETS) 
summary(modelETS)
ETS(A,N,N) 

Call:
 ets(y = dfETS) 

  Smoothing parameters:
    alpha = 0.954 

  Initial states:
    l = 93.1356 

  sigma:  1.2028

     AIC     AICc      BIC 
9107.716 9107.735 9123.031 

Training set error measures:
                      ME    RMSE       MAE         MPE     MAPE     MASE          ACF1
Training set -0.03953028 1.20184 0.8965985 -0.08424058 1.551493 1.001156 -0.0008160611

The best fitted Exponential Smoothing model has alpha 0.954, and its has AICc of 9107.735 and RMSE score is 1.20184 which is better than the performance of best fitted ARIMA model.

fcETS <- forecast(modelETS, h=100)
autoplot(fcETS)

Diagnosis and Forecasting Analysis

Upon training the dataset on ARIMA and ETS models we observe that they yield similar RMSE scores. This metric is further supported in the behavior of the 100 days forecast plots.

However, the RMSE score of the Exponential Smoothing (1.20184) is better than ARIMA model (1.203095) so, for our data the the Exponential Smoothing model (with alpha=0.954, beta=0, and gamma=0) is a better model than ARIMA (p=0, d=1, q=0).


References

  1. https://www.rdocumentation.org/
  2. Hyndman, R.J., & Athanasopoulos, G. (2021) Forecasting: principles and practice, 3rd edition, OTexts: Melbourne, Australia. OTexts.com/fpp3. Accessed on 4 December 2023. (https://otexts.com/fpp3/) **
  3. https://robjhyndman.com/expsmooth/ (Accessed on 4 December 2023)
  4. https://medium.com/analytics-vidhya/a-thorough-introduction-to-holt-winters-forecasting-c21810b8c0e6 (Accessed on 4 December 2023)
  5. https://www.machinelearningplus.com/time-series/arima-model-time-series-forecasting-python/ (Accessed on 5 December 2023)
  6. https://www.r-bloggers.com/2023/01/imputation-in-r-top-3-ways-for-imputing-missing-data/ (Accessed on 5 December 2023)
  7. https://tidyr.tidyverse.org/reference/fill.html (Accessed on 6 December 2023)

** We used the exact text from this book to maintain the accuracy of theoretical information provided.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIFByb2plY3QgMiAoRUFTIDUwOTogRmFsbCcyMykNCg0KIyMjIFsqKlByb2plY3QgVGl0bGUqKl17LnVuZGVybGluZX06IFRpbWUgU2VyaWVzIEFuYWx5c2lzICYgRm9yZWNhc3Rpbmcgb2YgT2lsIFNhbGVzDQoNCiMjIyBbVGVhbSBNZW1iZXJzXXsudW5kZXJsaW5lfToNCg0KMS4gIFN1amF5IFNocml2YXN0YXZhICg1MDQ5NjIyMSkgKHN1amF5c2hyKQ0KMi4gIFV0a2Fyc2ggTWF0aHVyICg1MDQ5NTEzMSkgKHVtYXRodXIpDQozLiAgVmVua2F0YSBMYWtzaG1pIEtyaXNobmEgVGVqYXN3aSBHdWRpbWV0bGEgKDUwNDk2Mzc4KSAodmd1ZGltZXQpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBJbXBvcnRpbmcgTGlicmFyaWVzIGFuZCBEYXRhDQoNCmBgYHtyfQ0KIyBWaXN1YWxpemF0aW9uIGFuZCBBbmFseXNpcw0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdnZGVuZHJvKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHBsb3RseSkNCnN1cHByZXNzV2FybmluZ3Moew0KICBsaWJyYXJ5KHJlYWR4bCkNCn0pDQoNCiMgTW9kZWxpbmcgYW5kIEluZmVyZW5jZQ0KbGlicmFyeShUU0EpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShhc3RzYSkNCmBgYA0KDQpgYGB7cn0NCiMgSW1wb3J0aW5kIERhdGEuDQojIE5PVEU6IFBsZWFzZSBzdG9yZSB0aGUgb2lsLmNzdiBmaWxlIGluIHRoZSBkaXJlY3Rvcnkgb2YgdGhpcyBmaWxlLg0KZGYgPSByZWFkLmNzdignb2lsLmNzdicpDQpzdW1tYXJ5KGRmKQ0KYGBgDQoNCkZyb20gdGhlIGFib3ZlIHN1bW1hcnkgd2UgY2FuIG9ic2VydmUgdGhhdCB0aGVyZSBhcmUgMTIxOCBkYXRhcG9pbnRzIGluIG91dCBkYXRhc2V0IGFuZCA0MyBtaXNzaW5nIHZhbHVlcy4gV2Ugd2lsbCBhZGRyZXNzIHRoZSBtaXNzaW5nIHZhbHVlcyBsYXRlciBvbi4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFBsb3R0aW5nIE9yaWdpbmFsIERhdGENCg0KYGBge3J9DQpwbG90MSA8LSBkZiB8Pg0KICBwbG90X2x5KHR5cGU9InNjYXR0ZXIiLCBtb2RlPSJsaW5lcyIpIHw+DQogIGFkZF90cmFjZSh4ID0gfmRhdGUsIHkgPSB+ZGNvaWx3dGljbywgbmFtZT0iRGFpbHkgU2FsZSIpIHw+DQogIGxheW91dChzaG93bGVnZW5kPUZBTFNFLCBwbG90X2JnY29sb3IgPSAid2hpdGUiKQ0Kb3B0aW9ucyh3YXJuPS0xKQ0KDQpwbG90MQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBJbXB1dGluZyBNaXNzaW5nIFZhbHVlDQoNCkJhc2VkIG9uIHRoZSBhbmFseXNpcyBvZiB0aGUgcGxvdCB3ZSBoYXZlIGRlY2lkZWQgdG8gaW1wdXRlIG1pc3NpbmcgdmFsdWUgd2l0aCB0aGUgbmV4dCB2YWx1ZSBvYnNlcnZlZCBpbiB0aGUgdGltZSBzZXJpZXMuDQoNCmBgYHtyfQ0KZGZfbmV3IDwtIGRmICU+JSBmaWxsKGRjb2lsd3RpY28sIC5kaXJlY3Rpb249InVwIikNCnN1bW1hcnkoZGZfbmV3KQ0KYGBgDQoNCldlIG5vdyBwbG90IHRoZSBpbXB1dGVkIGRhdGFzZXQuDQoNCmBgYHtyfQ0KcGxvdDIgPC0gZGZfbmV3IHw+DQogIHBsb3RfbHkodHlwZT0ic2NhdHRlciIsIG1vZGU9ImxpbmVzIikgfD4NCiAgYWRkX3RyYWNlKHggPSB+ZGF0ZSwgeSA9IH5kY29pbHd0aWNvLCBuYW1lPSJEYWlseSBTYWxlIikgfD4NCiAgbGF5b3V0KHNob3dsZWdlbmQ9RkFMU0UsIHBsb3RfYmdjb2xvciA9ICJ3aGl0ZSIpDQpvcHRpb25zKHdhcm49LTEpDQoNCnBsb3QyDQpgYGANCg0KVXBvbiBwbG90dGluZyB0aGUgaW1wdXRlZCBkYXRhc2V0IHdlIGNhbiBvYnNlcnZlIHRoYXQgdGhlcmUgaXMgbm8gdHJlbmQgb3Igc2Vhc29uYWxpdHkgb2JzZXJ2ZWQgb3ZlciB0aW1lLiBUaGlzIGlzIGEgdmVyeSBnb29kIGluc3RhbmNlIHRvIHRlc3QgRXhwb25lbnRpYWwgU21vb3RoaW5nIG1ldGhvZHMuDQoNCmBgYHtyfQ0KY29tcG9uZW50c19kZiA8LSBkZWNvbXBvc2UoZGZfbmV3KQ0KYGBgDQoNCkFzIGl0IGNhbiBiZSBzZWVuIGluIHRoZSBhYm92ZSBjb2RlIHBpZWNlIHRoZSBkYXRhIGhhcyBubyBzZWFzb25hbGl0eSBhcyB0aGUgdGltZSBzZXJpZXMgaW4gbm90IGRlY29tcG9zaWJsZS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIEVUUyBhbmQgSG9sdC1XaW50ZXJzIE1vZGVscw0KDQpGb3JlY2FzdHMgcHJvZHVjZWQgdXNpbmcgZXhwb25lbnRpYWwgc21vb3RoaW5nIG1ldGhvZHMgYXJlIHdlaWdodGVkIGF2ZXJhZ2VzIG9mIHBhc3Qgb2JzZXJ2YXRpb25zLCB3aXRoIHRoZSB3ZWlnaHRzIGRlY2F5aW5nIGV4cG9uZW50aWFsbHkgYXMgdGhlIG9ic2VydmF0aW9ucyBnZXQgb2xkZXIuIEluIG90aGVyIHdvcmRzLCB0aGUgbW9yZSByZWNlbnQgdGhlIG9ic2VydmF0aW9uIHRoZSBoaWdoZXIgdGhlIGFzc29jaWF0ZWQgd2VpZ2h0Lg0KDQojIyMgU2ltcGxlIEV4cG9uZW50aWFsIFNtb290aGluZw0KDQpUaGUgc2ltcGxlc3Qgb2YgZXhwb25lbnRpYWxseSBzbW9vdGhpbmcgbWV0aG9kcyBpcyBTaW1wbGUgRXhwb25lbnRpYWwgU21vb3RoaW5nIChTRVMpLiBUaGlzIG1ldGhvZCBpcyBzdWl0YWJsZSBmb3IgZm9yZWNhc3RpbmcgZGF0YSB3aXRoIG5vIGNsZWFyIHRyZW5kIG9yIHNlYXNvbmFsIHBhdHRlcm4uIEZvciBzaW1wbGUgZXhwb25lbnRpYWwgc21vb3RoaW5nLCB0aGUgb25seSBjb21wb25lbnQgaW5jbHVkZWQgaXMgdGhlIGxldmVsIGFzIG9ic2VydmVkIGluIHRoZSBiZWxvdyBmb3JlY2FzdGluZyBhbmQgc21vb3RoaW5nIGVxdWF0aW9uLiBUaGUgc21vb3RoaW5nIGVxdWF0aW9uIG9ubHkgdXNlcyBhbHBoYSB0byBpbmNsdWRlIHRoZSB0cmVuZCBjb21wb25lbnQgb2YgVGltZSBTZXJpZXMuDQoNCiFbXShpbWFnZXMvU2NyZWVuc2hvdCUyMDIwMjMtMTItMDYlMjAyMjAxMjAucG5nKQ0KDQojIyMgSG9sdC1XaW50ZXJzIE1ldGhvZHMNCg0KSG9sdCAoMTk1NykgYW5kIFdpbnRlcnMgKDE5NjApIGV4dGVuZGVkIEhvbHRcJ3MgbWV0aG9kIHRvIGNhcHR1cmUgc2Vhc29uYWxpdHkuIFRoZSBIb2x0LVdpbnRlcnMgc2Vhc29uYWwgbWV0aG9kIGNvbXByaXNlcyB0aGUgZm9yZWNhc3QgZXF1YXRpb24gYW5kIHRocmVlIHNtb290aGluZyBlcXVhdGlvbnMgLS0tIG9uZSBmb3IgdGhlIGxldmVsICRsX3QkIG9uZSBmb3IgdGhlIHRyZW5kICRiX3QkLCBhbmQgb25lIGZvciB0aGUgc2Vhc29uYWwgY29tcG9uZW50ICRzX3QkICx3aXRoIGNvcnJlc3BvbmRpbmcgc21vb3RoaW5nIHBhcmFtZXRlcnMgJFxhbHBoYSQsICRcYmV0YV4qJCwgYW5kICRcZ2FtbWEkIHJlc3BlY3RpdmVseS4NCg0KIyMjIyBBZGRpdGl2ZSBNZXRob2Q6DQoNCiFbXShpbWFnZXMvU2NyZWVuc2hvdCUyMDIwMjMtMTItMDYlMjAyMjEwNDgucG5nKQ0KDQojIyMjIE11bHRpcGxpY2F0aXZlIE1ldGhvZDoNCg0KIVtdKGltYWdlcy9TY3JlZW5zaG90JTIwMjAyMy0xMi0wNiUyMDIyMTEwNy5wbmcpDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBNb2RlbCBUcmFpbmluZw0KDQpBcyB0aGUgZGF0YSBkb2Vzbid0IGhhdmUgc2Vhc29uYWxpdHkgaW4gaXQgKGV2aWRlbnQgZnJvbSB0aGUgZGVjb21wb3NpdGlvbiBvZiBkYXRhKSB3ZSBjYW4gc2FmZWx5IHNheSB0aGF0IEhvbHQtV2ludGVycyBtZXRob2QgaXMgb2YgbGVzc2VyIHVzZSBoZXJlLiBTbyB3ZSBhcmUgZ29pbmcgdG8gY29tcGFyZSAyIG1vZGVsczoNCg0KMS4gIEFSSU1BICh0cmFpbmVkIHdpdGhvdXQgc3BlY2lmeWluZyBwLCBkLCBhbmQgcSkNCjIuICBFeHBvbmVudGlhbCBTbW9vdGhpbmcNCg0KIyMjIEFSSU1BIE1vZGVsDQoNCmBgYHtyfQ0KbW9kZWxBUklNQSA8LSBhdXRvLmFyaW1hKGRmX25ld1snZGNvaWx3dGljbyddKQ0Kc3VtbWFyeShtb2RlbEFSSU1BKQ0KYGBgDQoNCkFzIG9ic2VydmVkLCB0aGUgYmVzdCBmaXQgQVJJTUEgbW9kZWwgaXMgQVJJTUEocD0wLCBkPTEsIHE9MCkgd2l0aCBBSUNjMzkwNi43MyBhbmQgUk1TRSAxLjIwMzA5NS4NCg0KYGBge3J9DQpmY0FSSU1BIDwtIGZvcmVjYXN0KG1vZGVsQVJJTUEsIGg9MTAwKQ0KYXV0b3Bsb3QoZmNBUklNQSkNCmBgYA0KDQojIyMgRXhwb25lbnRpYWwgU21vb3RoaW5nIG1vZGVsDQoNCmBgYHtyfQ0KZGZFVFMgPC0gYXMudHMoZGZfbmV3WydkY29pbHd0aWNvJ10pDQptb2RlbEVUUyA8LSBldHMoZGZFVFMpIA0Kc3VtbWFyeShtb2RlbEVUUykNCmBgYA0KDQpUaGUgYmVzdCBmaXR0ZWQgRXhwb25lbnRpYWwgU21vb3RoaW5nIG1vZGVsIGhhcyBhbHBoYSAwLjk1NCwgYW5kIGl0cyBoYXMgQUlDYyBvZiA5MTA3LjczNSBhbmQgUk1TRSBzY29yZSBpcyAxLjIwMTg0IHdoaWNoIGlzIGJldHRlciB0aGFuIHRoZSBwZXJmb3JtYW5jZSBvZiBiZXN0IGZpdHRlZCBBUklNQSBtb2RlbC4NCg0KYGBge3J9DQpmY0VUUyA8LSBmb3JlY2FzdChtb2RlbEVUUywgaD0xMDApDQphdXRvcGxvdChmY0VUUykNCmBgYA0KDQojIyMgRGlhZ25vc2lzIGFuZCBGb3JlY2FzdGluZyBBbmFseXNpcw0KDQpVcG9uIHRyYWluaW5nIHRoZSBkYXRhc2V0IG9uIEFSSU1BIGFuZCBFVFMgbW9kZWxzIHdlIG9ic2VydmUgdGhhdCB0aGV5IHlpZWxkIHNpbWlsYXIgUk1TRSBzY29yZXMuIFRoaXMgbWV0cmljIGlzIGZ1cnRoZXIgc3VwcG9ydGVkIGluIHRoZSBiZWhhdmlvciBvZiB0aGUgMTAwIGRheXMgZm9yZWNhc3QgcGxvdHMuDQoNCkhvd2V2ZXIsIHRoZSBSTVNFIHNjb3JlIG9mIHRoZSBFeHBvbmVudGlhbCBTbW9vdGhpbmcgKDEuMjAxODQpIGlzIGJldHRlciB0aGFuIEFSSU1BIG1vZGVsICgxLjIwMzA5NSkgc28sIGZvciBvdXIgZGF0YSB0aGUgdGhlIEV4cG9uZW50aWFsIFNtb290aGluZyBtb2RlbCAod2l0aCBhbHBoYT0wLjk1NCwgYmV0YT0wLCBhbmQgZ2FtbWE9MCkgaXMgYSBiZXR0ZXIgbW9kZWwgdGhhbiBBUklNQSAocD0wLCBkPTEsIHE9MCkuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBSZWZlcmVuY2VzDQoNCjEuICA8aHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnLz4NCjIuICBIeW5kbWFuLCBSLkouLCAmIEF0aGFuYXNvcG91bG9zLCBHLiAoMjAyMSkgKkZvcmVjYXN0aW5nOiBwcmluY2lwbGVzIGFuZCBwcmFjdGljZSosIDNyZCBlZGl0aW9uLCBPVGV4dHM6IE1lbGJvdXJuZSwgQXVzdHJhbGlhLiBPVGV4dHMuY29tL2ZwcDMuIEFjY2Vzc2VkIG9uIDQgRGVjZW1iZXIgMjAyMy4gKDxodHRwczovL290ZXh0cy5jb20vZnBwMy8+KSBcKlwqDQozLiAgIDxodHRwczovL3JvYmpoeW5kbWFuLmNvbS9leHBzbW9vdGgvPiAoQWNjZXNzZWQgb24gNCBEZWNlbWJlciAyMDIzKQ0KNC4gIDxodHRwczovL21lZGl1bS5jb20vYW5hbHl0aWNzLXZpZGh5YS9hLXRob3JvdWdoLWludHJvZHVjdGlvbi10by1ob2x0LXdpbnRlcnMtZm9yZWNhc3RpbmctYzIxODEwYjhjMGU2PiAoQWNjZXNzZWQgb24gNCBEZWNlbWJlciAyMDIzKQ0KNS4gIDxodHRwczovL3d3dy5tYWNoaW5lbGVhcm5pbmdwbHVzLmNvbS90aW1lLXNlcmllcy9hcmltYS1tb2RlbC10aW1lLXNlcmllcy1mb3JlY2FzdGluZy1weXRob24vPiAoQWNjZXNzZWQgb24gNSBEZWNlbWJlciAyMDIzKQ0KNi4gIDxodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8yMDIzLzAxL2ltcHV0YXRpb24taW4tci10b3AtMy13YXlzLWZvci1pbXB1dGluZy1taXNzaW5nLWRhdGEvPiAoQWNjZXNzZWQgb24gNSBEZWNlbWJlciAyMDIzKQ0KNy4gIDxodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2ZpbGwuaHRtbD4gKEFjY2Vzc2VkIG9uIDYgRGVjZW1iZXIgMjAyMykNCg0KXCpcKiBXZSB1c2VkIHRoZSBleGFjdCB0ZXh0IGZyb20gdGhpcyBib29rIHRvIG1haW50YWluIHRoZSBhY2N1cmFjeSBvZiB0aGVvcmV0aWNhbCBpbmZvcm1hdGlvbiBwcm92aWRlZC4NCg==